package com.huawei.codearts.service;

import com.huawei.codearts.dao.OrderDao;
import com.huawei.codearts.model.Order;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.math.BigDecimal;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Service
public class OrderService {

    @Autowired
    private OrderDao orderDao;

//    @Autowired
//    public OrderService(OrderDao orderDao) {
//        this.orderDao = orderDao;
//    }

    /** 单条数据 */
    public Object getOne(int id) {
        Object[] params = new Object[]{id};
        Order order = orderDao.getOneId(params);
        // 处理数据
        // 时间戳转换成日期
        long createtime = order.getCreateTime();
        Date date = new Date(createtime * 1000);  // 把秒转换成毫秒
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String createTimeFormat = sdf.format(date);
        order.setCreateTimeFormat(createTimeFormat);

        // 自定义map
        HashMap<String, Object> dataMap = new HashMap<>();
        dataMap.put("id", order.getId());
        dataMap.put("number", order.getNumber());
        dataMap.put("sort", order.getSort());
        dataMap.put("memberId", order.getMemberId());
        dataMap.put("memberName", order.getMemberName());
        dataMap.put("price", order.getPrice());
        dataMap.put("createTime", order.getCreateTime());
        dataMap.put("createTimeFormat", createTimeFormat);

        return dataMap;
    }

    /** 多条数据 */
    public Object getList() {

        // 返回数据
        List<Object> dataList = new ArrayList<>();
        // 从数据库获取的数据
        Object[] params = new Object[]{1, 2};
        List<Order> orderList = orderDao.getList(params);
        int i = 0;
        for(Order item : orderList) {
            i ++;
            // 处理数据
            // 时间戳转换成日期
            long createtime = item.getCreateTime();
            Date date = new Date(createtime * 1000);  // 把秒转换成毫秒
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            String createTimeFormat = sdf.format(date);
            item.setCreateTimeFormat(createTimeFormat);

            // 定义map
            HashMap<String, Object> dataMap = new HashMap<>();
            dataMap.put("id", item.getId());
            dataMap.put("number", item.getNumber());
            dataMap.put("sort", item.getSort());
            dataMap.put("memberId", item.getMemberId());
            dataMap.put("memberName", item.getMemberName());
            dataMap.put("price", item.getPrice());
            dataMap.put("createTime", item.getCreateTime());
            dataMap.put("createtimeFormat", createTimeFormat);

            dataList.add(dataMap);
        }

        return dataList;
    }

    /** 添加 */
    public Integer insert() {
        Order order = new Order();
        order.setNumber("11240126505800001");
        order.setSort("00001");
        order.setMemberId(4);
        order.setMemberName("ddd");
        order.setPrice(BigDecimal.valueOf(666));
        long timestamp = System.currentTimeMillis();
        order.setCreateTime(timestamp);
        return orderDao.insert(order);
    }

    /** 更新 */
    public Integer update(int id) {
        Order order = new Order();
        order.setId(id);
        order.setNumber("11240126505800001");
        order.setSort("00001");
        order.setMemberId(4);
        order.setMemberName("ccc");
        order.setPrice(BigDecimal.valueOf(999));
        long timestamp = System.currentTimeMillis();
        order.setCreateTime(timestamp);
        return orderDao.update(order);
    }

    /** 删除 */
    public Integer del(int id) {
        return orderDao.del(id);
    }


    // 手动事务管理器
    @Autowired
    private PlatformTransactionManager transactionManager;  // 事务
//    private DataSourceTransactionManager transactionManager;  // 事务

    /** 手动数据库事务 & 锁 */
    public boolean updateWithTx(int id) throws Exception {
        boolean resbool = false;
//        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        // 创建事务定义对象
        //DefaultTransactionDefinition是TransactionDefinition 的实现类
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        // 设置是否只读，false支持事务
        def.setReadOnly(false);
        // 设置事务隔离级别，可重复读mysql默认级别
        def.setIsolationLevel(TransactionDefinition.ISOLATION_REPEATABLE_READ);
        // 设置事务传播行为，必须有事务
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        // 通过事务管理器，拿取对应的状态对象，并在后面给出他作为参数，来保存对应的状态，这里主要是为了开启事务，要不然哪里来的开启事务呢（内部进行开启事务，一般并不是通过关系自动提交来操作的）
        TransactionStatus status = transactionManager.getTransaction(def);

        /*
         * 可以添加分布式锁 比如redis的
         * set money = ? 需要加锁 比如：排他锁 FOR UPDATE 或者 乐观锁
         * set money = money - 100 这种已经是锁了
         * 乐观锁是不用加锁 谁都能读 但在更新的时候加上version条件 如下：
         * update qhdzs_member set money = ?, version = version + 1 where id = ? and version = $version
         */

        try {
            // 模拟转账事务
            Order order = orderDao.getOneTx(id);
            BigDecimal price = order.getPrice();
            BigDecimal priceSum = price.add(BigDecimal.valueOf(100));
            System.out.println("price:" + price);
            System.out.println("priceSum:" + priceSum);

//            Thread.sleep(6000);

            Order orderModel = new Order();
            orderModel.setId(id);
            orderModel.setPrice(priceSum);
            String sql0 = "update qhdzs_orders set price = ? where id = ?";  // 这种赋值形式 就需要查询的时候加锁 比如上面的 FOR UPDATE 或者乐观锁
            int res0 = orderDao.updateWithTx(sql0, orderModel);
            System.out.println("res0: " + res0);

            orderModel.setId(222);
            orderModel.setPrice(BigDecimal.valueOf(10));
            String sql1 = "update qhdzs_orders set price = price - ? where id = ?";
            int res1 = orderDao.updateWithTx(sql1, orderModel);
            System.out.println("res1: " + res1);

//            Thread.sleep(5000);
            // 模拟网络异常
            System.out.println(10 / 0);

            orderModel.setId(223);
            orderModel.setPrice(BigDecimal.valueOf(10));
            String sql2 = "update qhdzs_orders set price = price + ? where id = ?";
            int res2 = orderDao.updateWithTx(sql2, orderModel);
            System.out.println("res2: " + res2);

            //2.提交事务
            transactionManager.commit(status);
            resbool = true;
        } catch (Throwable e) {
            System.out.println("事务回滚...");
            // 回滚事务
            transactionManager.rollback(status);
//            e.printStackTrace();
            throw e;
        }

        return resbool;
    }

    /**
     * spring事务管理器 开启事务
     * 直接添加 @Transactional 直注解即可
     */
    @Transactional
    public boolean springWithTx(int id) {
        boolean resbool = true;
        /*
         * 可以添加分布式锁 比如redis的
         * set money = ? 需要加锁 比如：排他锁 FOR UPDATE 或者 乐观锁
         * set money = money - 100 这种已经是锁了
         * 乐观锁是不用加锁 谁都能读 但在更新的时候加上version条件 如下：
         * update qhdzs_member set money = ?, version = version + 1 where id = ? and version = $version
         */

        // 模拟转账事务
        Order order = orderDao.getOneTx(id);
        BigDecimal price = order.getPrice();
        BigDecimal priceSum = price.add(BigDecimal.valueOf(100));
        System.out.println("price:" + price);
        System.out.println("priceSum:" + priceSum);

//        Thread.sleep(6000);

        Order orderModel = new Order();
        orderModel.setId(id);
        orderModel.setPrice(priceSum);
        String sql0 = "update qhdzs_orders set price = ? where id = ?";  // 这种赋值形式 就需要查询的时候加锁 比如上面的 FOR UPDATE 或者乐观锁
        int res0 = orderDao.updateWithTx(sql0, orderModel);
        System.out.println("res0: " + res0);

        orderModel.setId(222);
        orderModel.setPrice(BigDecimal.valueOf(100));
        String sql1 = "update qhdzs_orders set price = price - ? where id = ?";
        int res1 = orderDao.updateWithTx(sql1, orderModel);
        System.out.println("res1: " + res1);

        // 模拟网络异常
//        System.out.println(10 / 0);

        orderModel.setId(223);
        orderModel.setPrice(BigDecimal.valueOf(100));
        String sql2 = "update qhdzs_orders set price = price + ? where id = ?";
        int res2 = orderDao.updateWithTx(sql2, orderModel);
        System.out.println("res2: " + res2);

        return resbool;
    }

}
